/*
* @Descripttion: beyondstorage实现的文件管理
* @version:
* @Author: lfzxs@qq.com
* @Date: 2022-09-27 15:20:31
 * @LastEditors: lfzxs@qq.com
 * @LastEditTime: 2022-12-06 16:04:52
* @DOC: https://beyondstorage.dev/zh-CN/docs/go-storage/operations/storager/index
*/
package file

import (
	"bytes"
	"errors"
	"io"
	"log"
	"strings"

	"github.com/gogf/gf/v2/net/ghttp"
	_ "go.beyondstorage.io/services/fs/v4"
	_ "go.beyondstorage.io/services/minio"
	"go.beyondstorage.io/v5/pairs"
	"go.beyondstorage.io/v5/services"
	"go.beyondstorage.io/v5/types"

	hctypes "gitee.com/g_boot/gfextend/types"
)

var defaultFileName = "create_Dir_default_makefile.txt"

type beyondstorageFile struct {
	Store   types.Storager
	Connstr string
}

func CreateStorager(connStr string) (result *beyondstorageFile, err error) {
	store, err := services.NewStoragerFromString(connStr)
	if err != nil {
		return nil, err
	}
	result = &beyondstorageFile{Store: store, Connstr: connStr}
	return result, nil
}

func (self *beyondstorageFile) WriteUploadFile(path string, file *ghttp.UploadFile, fileType string) (writeSize int64, err error) {

	closer, _ := file.Open()

	writeSize, err = self.WriteFile(path, closer, file.Size)

	return
}

// 写入文件
func (self *beyondstorageFile) WriteFile(path string, r io.Reader, size int64, pairs ...hctypes.Pair) (writeSize int64, err error) {

	writeSize, err = self.Store.Write(path, r, size)

	return
}

// 追加文件
func (self *beyondstorageFile) AppendFile(path string, r io.Reader, size int64, pairs ...hctypes.Pair) (writeSize int64, err error) {
	appender, ok := self.Store.(types.Appender)
	if !ok {
		log.Printf("Appender unimplemented")
	}
	o, err := self.Store.Stat(path)

	if err != nil {
		log.Printf("Stat %v: %v", path, err)
		return 0, err
	}

	n, err := appender.WriteAppend(o, r, size)

	err = appender.CommitAppend(o)
	if err != nil {
		log.Printf("CommitAppend %v: %v", path, err)
		return 0, err
	}

	return n, err
}

// 读取文件
func (self *beyondstorageFile) ReadFile(path string) (data []byte, err error) {
	return self.ReadRange(path, 0, hctypes.MaxFileSize)
}

// 随机读取
func (self *beyondstorageFile) ReadRange(path string, start int64, size int64) (data []byte, err error) {
	//https://github.com/beyondstorage/go-storage-example/blob/master/read.go
	var buf bytes.Buffer
	_, err = self.Store.Read(path, &buf, pairs.WithOffset(start), pairs.WithSize(size))
	if err != nil {
		log.Printf("ReadRange %v: %v", path, err)
		return nil, err
	}

	return buf.Bytes(), err

}

// 删除文件
func (self *beyondstorageFile) RemoveFile(path string) (err error) {
	self.Store.Delete(path)
	return
}

// 重命名文件
func (self *beyondstorageFile) RenameFile(path string, name string) (err error) {
	pathArr := strings.Split(path, "/")
	newpath := ""

	for i := 0; i < len(pathArr)-1; i++ {
		newpath = newpath + pathArr[i] + "/"
	}
	newpath = newpath + name
	self.MoveFile(path, newpath)
	return
}

// 文件列表
func (self *beyondstorageFile) ListFile(path string) (oi *types.ObjectIterator, err error) {
	o, err := self.Store.List(path)
	return o, err
}

// 判断文件是否存在
func (self *beyondstorageFile) Exists(path string) (yes bool) {
	o, err := self.Store.Stat(path)
	if o != nil {
		return true
	}
	if err != nil {
		return false
	}
	return
}

// 获取文件信息
func (self *beyondstorageFile) FileInfo(path string) (pairs []hctypes.Pair, err error) {
	o, err := self.Store.Stat(path)

	if err != nil {
		log.Printf("FileInfo %v: %v", path, err)
		return nil, err
	}

	// 大小
	len, suc := o.GetContentLength()
	if suc {
		pairs = append(pairs, hctypes.WithSize(len))
	}
	//类型
	typ, suc := o.GetContentType()
	if suc {
		pairs = append(pairs, hctypes.WithContentType(typ))
	}

	md5, suc := o.GetContentMd5()
	if suc {
		pairs = append(pairs, hctypes.WithContentMd5(md5))
	}

	//修改时间
	modifyTime, suc := o.GetLastModified()
	if suc {
		pairs = append(pairs, hctypes.WithModifyTime(modifyTime))
	}

	return
}

// 复制文件
func (self *beyondstorageFile) CopyFile(source string, dest string) (err error) {

	copier, ok := self.Store.(types.Copier)
	if !ok {
		msg := "Copier unimplemented"
		log.Printf(msg)
		err = errors.New(msg)
		return
	}
	err = copier.Copy(source, dest)

	if err != nil {
		log.Printf("CopeFile %v --> %v: %v ", source, dest, err)
	}
	return
}

// 移动文件
func (self *beyondstorageFile) MoveFile(source string, dest string) (err error) {

	mover, ok := self.Store.(types.Mover)
	if !ok {
		err = self.CopyFile(source, dest)
		self.RemoveFile(source)
		if err != nil {
			log.Printf("Use CopeFile %v --> %v: %v ", source, dest, err)
		}
		return
	}

	err = mover.Move(source, dest)

	if err != nil {
		log.Printf("MoveFile %v --> %v: %v ", source, dest, err)
	}
	return
}

// 创建目录
func (self *beyondstorageFile) CreateDir(path string, dirName string) (dirpath string, err error) {

	text := "Create Dir default makefile"
	buf := []byte(text)
	r := bytes.NewReader(buf)
	dirpath = path + "/" + dirName + "/"
	path = dirpath + defaultFileName
	_, err = self.Store.Write(path, r, int64(len(text)))
	if err != nil {
		log.Printf("CreateDir %v --> %v: %v ", path, dirName, err)
		return "", err
	}
	return
}

// 移动目录
func (self *beyondstorageFile) MoveDir(source string, dest string) (err error) {
	it, err := self.Store.List(source)
	for {
		// User can retrieve all the objects by `Next`. `types.IterateDone` will be returned while there is no item anymore.
		o, err := it.Next()
		if err != nil && !errors.Is(err, types.IterateDone) {
			log.Printf("Next: %v", err)
		}

		if err != nil {
			log.Printf("列表结束")
			self.RemoveDir(source + "/")
			break
		}

		// log.Printf("object oldpath: %v", o.Path)
		ob, err := self.Store.Stat(o.Path)
		//类型
		typ, suc := ob.GetContentType()
		if suc {
			log.Printf("object type: %v", typ)
			oldpathArr := strings.Split(source, "/")
			pathArr := strings.Split(o.Path, "/")
			j := 0
			var newPath string
			for i := 0; i < len(oldpathArr); i++ {
				if pathArr[0] == "" {
					if oldpathArr[i] == pathArr[i] {
						j++
					}
				} else {
					if i > 0 && oldpathArr[i] == pathArr[i-1] {
						j++
					}
				}
			}
			for i := j; i < len(pathArr); i++ {
				newPath = newPath + "/" + pathArr[i]
			}
			// log.Printf("object newPath: %v", newPath)
			self.MoveFile(o.Path, dest+newPath)
			// self.CopyDel(o.Path, dest)
			self.RemoveDir(o.Path + "/")
		} else {
			self.CopyDel(o.Path, dest)
			self.RemoveDir(o.Path + "/")
		}
	}
	return
}

// 递归拷贝删除文件文件夹
func (self *beyondstorageFile) CopyDel(source string, dest string) (err error) {
	it, err := self.Store.List(source)
	for {
		// User can retrieve all the objects by `Next`. `types.IterateDone` will be returned while there is no item anymore.
		o, err := it.Next()
		if err != nil && !errors.Is(err, types.IterateDone) {
			log.Printf("Next: %v", err)
		}
		if err != nil {
			log.Printf("列表结束")
			self.RemoveDir(source + "/")
			break
		}
		oldpathArr := strings.Split(source, "/")
		log.Printf("object path: %v", o.Path)
		ob, err := self.Store.Stat(o.Path)
		//类型
		typ, suc := ob.GetContentType()
		if suc {
			log.Printf("object type: %v", typ)
			pathArr := strings.Split(o.Path, "/")
			self.MoveFile(o.Path, dest+"/"+oldpathArr[len(oldpathArr)-1]+"/"+pathArr[len(pathArr)-1])
		} else {
			self.CopyDel(o.Path, dest+"/"+oldpathArr[len(oldpathArr)-1])
			self.RemoveDir(o.Path + "/")
		}
	}
	return
}

// 删除目录
func (self *beyondstorageFile) RemoveDir(path string) (err error) {
	self.Store.Delete(path)
	return
}

// 目录信息
func (self *beyondstorageFile) DirInfo(path string) (pairs []hctypes.Pair, err error) {

	o, err := self.Store.Stat(path)

	if err != nil {
		log.Printf("FileInfo %v: %v", path, err)
		return nil, err
	}
	// 大小
	len, suc := o.GetContentLength()
	if suc {
		pairs = append(pairs, hctypes.WithSize(len))
	}
	//类型
	typ, suc := o.GetContentType()
	if suc {
		pairs = append(pairs, hctypes.WithContentType(typ))
	}

	md5, suc := o.GetContentMd5()
	if suc {
		pairs = append(pairs, hctypes.WithContentMd5(md5))
	}

	//修改时间
	modifyTime, suc := o.GetLastModified()
	if suc {
		pairs = append(pairs, hctypes.WithModifyTime(modifyTime))
	}
	//目录信息
	pathinof := o.GetPath()
	pairs = append(pairs, hctypes.WithPath(pathinof))

	return
}

// 重命名文件夹
func (self *beyondstorageFile) RenameDir(path string, name string) (err error) {
	oldpathArr := strings.Split(path, "/")
	var newPath string
	for i := 0; i < len(oldpathArr)-1; i++ {
		newPath = newPath + oldpathArr[i] + "/"
	}
	newPath = newPath + name
	// log.Printf("object newPath: %v", newPath)
	error := self.MoveDir(path, newPath)

	return error
}


func (self *beyondstorageFile) GetConnstr() string {
	return self.Connstr
}
